package com.boylegu.springboot_vue.controller.pagination;

import java.util.HashMap;
import java.util.Map;

import com.boylegu.springboot_vue.entities.Students;
import com.boylegu.springboot_vue.entities.Teachers;
import com.boylegu.springboot_vue.entities.Users;
import com.boylegu.springboot_vue.service.StudentService;
import com.boylegu.springboot_vue.service.TeacherService;
import com.boylegu.springboot_vue.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Page;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/*
    Resolve due to @Autowired lead to NullPointerException problem

    Description：
    1. It's limited to general class to invoke spring bean Object.
    2. And This makes the sub package easy to scan by spring boot.

                                                      ———— @Boyle.gu
*/
@Component
class SpringUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext = null;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

        if (SpringUtil.applicationContext == null) {

            SpringUtil.applicationContext = applicationContext;

        }
    }

    public static ApplicationContext getApplicationContext() {

        return applicationContext;

    }

    public static Object getBean(String name) {

        return getApplicationContext().getBean(name);

    }

    public static <T> T getBean(Class<T> clazz) {

        return getApplicationContext().getBean(clazz);

    }

    public static <T> T getBean(String name, Class<T> clazz) {

        return getApplicationContext().getBean(name, clazz);

    }
}


interface Types {

    public Integer getCount();

    public Integer getPageNumber();

    public Long getTotal();

    public Object getContent();
}

class BasePaginationInfo {

    public Pageable pageable;

    public BasePaginationInfo(String sexName, String emailName, Pageable pageable) {

        this.pageable = pageable;

//        this.sex = sexName;
//
//        this.email = emailName;
    }

    public BasePaginationInfo(Pageable pageable) {
        this.pageable = pageable;
    }
}

class AllUsers extends BasePaginationInfo implements Types {

    @Autowired
    public UserService userService = SpringUtil.getBean(UserService.class);;

    public AllUsers(String sexName, String emailName, Pageable pageable) { //String sexName, String emailName,

        super(sexName, emailName, pageable);

    }

    public AllUsers(Pageable pageable) { //String sexName, String emailName,
        super(pageable);
    }

    public Page<Users> query() {

        return this.userService.findAll(

                this.pageable
        );
    }

    public Integer getCount() {
        return this.query().getSize();
    }

    public Integer getPageNumber() {

        return this.query().getNumber();

    }

    public Long getTotal() {
        return this.query().getTotalElements();
    }

    public Object getContent() {
        return this.query().getContent();
    }
}

class AllStudents extends BasePaginationInfo implements Types {

    @Autowired
    public StudentService studentService = SpringUtil.getBean(StudentService.class);;

    public AllStudents(Pageable pageable) { //String sexName, String emailName,
        super(pageable);
    }

    public Page<Students> query() {

        return this.studentService.findAll(

                this.pageable
        );
    }

    public Integer getCount() {
        return this.query().getSize();
    }

    public Integer getPageNumber() {

        return this.query().getNumber();

    }

    public Long getTotal() {
        return this.query().getTotalElements();
    }

    public Object getContent() {
        return this.query().getContent();
    }
}

class AllTeachers extends BasePaginationInfo implements Types {

    @Autowired
    public TeacherService teacherService = SpringUtil.getBean(TeacherService.class);;

    public AllTeachers(Pageable pageable) { //String sexName, String emailName,
        super(pageable);
    }

    public Page<Teachers> query() {

        return this.teacherService.findAll(

                this.pageable
        );
    }

    public Integer getCount() {
        return this.query().getSize();
    }

    public Integer getPageNumber() {

        return this.query().getNumber();

    }

    public Long getTotal() {
        return this.query().getTotalElements();
    }

    public Object getContent() {
        return this.query().getContent();
    }
}

class SexEmailType extends BasePaginationInfo implements Types {

    public SexEmailType(String sexName, String emailName, Pageable pageable) {

        super(sexName, emailName, pageable);

    }

    public Page<Users> query() {

        return null;
//        return this.instance.findBySexAndEmailContains(
//
//                this.sex,
//
//                this.email,
//
//                this.pageable
//        );
    }

    public Integer getCount() {
        return this.query().getSize();
    }

    public Integer getPageNumber() {

        return this.query().getNumber();

    }

    public Long getTotal() {
        return this.query().getTotalElements();
    }

    public Object getContent() {
        return this.query().getContent();
    }


}

class SexType extends BasePaginationInfo implements Types {

    public SexType(String sexName, String emailName, Pageable pageable) { //String sexName, String emailName,

        super(sexName, emailName, pageable);
    }

    public Page<Users> query() {

        return null;
//        return this.instance.findBySex(
//
//                this.sex,
//
//                this.pageable
//        );
    }

    public Integer getCount() {
        return this.query().getSize();
    }

    public Integer getPageNumber() {

        return this.query().getNumber();

    }

    public Long getTotal() {
        return this.query().getTotalElements();
    }

    public Object getContent() {
        return this.query().getContent();
    }
}


public class PaginationFormatting {

    public static final String TYPE_USER = "user";
    public static final String TYPE_STUDENT = "student";
    public static final String TYPE_TEACHER = "teacher";

    private PaginationMultiTypeValuesHelper multiValue = new PaginationMultiTypeValuesHelper();

    private Map<String, PaginationMultiTypeValuesHelper> results = new HashMap<>();

    public Map<String, PaginationMultiTypeValuesHelper> filterQuery(String sex, String email, Pageable pageable) {

        Types typeInstance;

        if (sex.length() == 0 && email.length() == 0) {

            typeInstance = new AllUsers(sex, email, pageable);

        } else if (sex.length() > 0 && email.length() > 0) {

            typeInstance = new SexEmailType(sex, email, pageable);

        } else {
            typeInstance = new SexType(sex, email, pageable);
        }

        this.multiValue.setCount(typeInstance.getCount());

        this.multiValue.setPage(typeInstance.getPageNumber() + 1);

        this.multiValue.setResults(typeInstance.getContent());

        this.multiValue.setTotal(typeInstance.getTotal());

        this.results.put("data", this.multiValue);

        return results;
    }

    public Map<String, PaginationMultiTypeValuesHelper> filterQuery(Pageable pageable, String type) {

        Types typeInstance = null;
        switch (type) {
            case TYPE_USER: {
                typeInstance = new AllUsers(pageable);
                break;
            }
            case TYPE_STUDENT: {
                typeInstance = new AllStudents(pageable);
                break;
            }
            case TYPE_TEACHER: {
                typeInstance = new AllTeachers(pageable);
                break;
            }
        }

        if (typeInstance != null) {
            this.multiValue.setCount(typeInstance.getCount());

            this.multiValue.setPage(typeInstance.getPageNumber() + 1);

            this.multiValue.setResults(typeInstance.getContent());

            this.multiValue.setTotal(typeInstance.getTotal());

            this.results.put("data", this.multiValue);
        }

        return results;
    }

}